package bean

import (
	"bytes"
	"container/list"
	_ "crypto/aes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"strconv"
)

const (
	KEY_ID = "_PK_"
)

func Version() string {
	return "0.0.1"
}

type Bean struct {
	Data map[string]interface{}
}

func NewBean(pk string) *Bean {
	bean := Bean{
		Data: map[string]interface{}{},
	}
	if len(pk) > 0 {
		bean.SetId(pk)
	}
	return &bean
}

func NewBean2(m map[string]interface{}) *Bean {
	bean := Bean{
		Data: m,
	}
	return &bean
}

func (b *Bean) SetMap(m map[string]interface{}) *Bean {
	if m != nil {
		for k, v := range m {
			b.Set(k, v)
		}
	}
	return b
}

func (b *Bean) SetId(id string) *Bean {
	b.Set(KEY_ID, id)
	return b
}

func (b *Bean) GetId() string {
	return b.GetStr(KEY_ID)
}

func (b *Bean) Keys() []string {
	var keys []string
	for k, _ := range b.Data {
		keys = append(keys, k)
	}
	return keys
}

// get bean map
func (b *Bean) GetMap() map[string]interface{} {
	return b.Data
}

func (b *Bean) GetBean(key string) *Bean {
	if b.Contains(key) {
		return (b.Get(key)).(*Bean)
	}
	return nil
}

func (b *Bean) Values() []interface{} {
	var vals []interface{}
	for _, v := range b.Data {
		vals = append(vals, v)
	}
	return vals
}

func (b *Bean) GetStr(key string, def ...string) string {
	if str, ok := b.Data[key].(string); ok {
		return str
	}
	if len(def) > 0 {
		return def[0]
	}
	return ""
}

func (b *Bean) GetInt(key string, def ...int) int {
	if b.Data[key] != nil {
		switch b.Data[key].(type) {
		case string:
			v, err := strconv.Atoi(b.Data[key].(string))
			if err != nil {
				if len(def) > 0 {
					return def[0]
				}
			}
			return v
		default:
			return b.Data[key].(int)
		}
	}
	if len(def) > 0 {
		return def[0]
	}
	return 0
}

func (b *Bean) GetInt8(key string, def ...int8) int8 {
	return If(b.Data[key] != nil, b.Data[key], def[0]).(int8)
}

func (b *Bean) GetInt64(key string, def ...int64) int64 {
	return If(b.Data[key] != nil, b.Data[key], def[0]).(int64)
}

func (b *Bean) GetFloat32(key string, def ...float32) float32 {
	v := b.Data[key]
	if v != nil {
		switch v.(type) {
		case float32, float64:
			return v.(float32)
		case string:
			if f, err := strconv.ParseFloat(v.(string), 32); err == nil {
				return float32(f)
			}
		}
	}
	if len(def) > 0 {
		return def[0]
	}
	return 0
}

func (b *Bean) GetBool(key string, def ...bool) bool {
	if b.Data[key] != nil {
		return b.Data[key].(bool)
	}
	if len(def) > 0 {
		return def[0]
	}
	return false
}

func (b *Bean) GetOrDef(key string, def ...interface{}) interface{} {
	return If(b.Data[key] != nil, b.Data[key], def[0])
}

func (b *Bean) Get(key string) interface{} {
	return b.Data[key]
}

func (b *Bean) Set(key string, value interface{}) *Bean {
	b.Data[key] = value
	return b
}

func If(condition bool, trueVal, falseVal interface{}) interface{} {
	if condition {
		return trueVal
	}
	return falseVal
}

func (b *Bean) Contains(key string) bool {
	_, ok := b.Data[key]
	return ok
}

func (b *Bean) GetList(key string) *list.List {
	if b.Contains(key) {
		return (b.Data[key]).(*list.List)
	} else {
		return list.New()
	}
}

func (b *Bean) GetBeans(key string) []*Bean {
	if b.Contains(key) {
		return (b.Data[key]).([]*Bean)
	} else {
		return nil
	}
}

func (b *Bean) GetArray(key string) []interface{} {
	if b.Contains(key) {
		return (b.Data[key]).([]interface{})
	} else {
		return nil
	}
}

func (this *Bean) UnmarshalJSON(bytes []byte) error {
	panic("implement me")
}

func (this *Bean) MarshalJSON() ([]byte, error) {
	return json.Marshal(&this.Data)
}

func (b *Bean) GetDataSource() string {
	return b.GetStr("SERV_DATA_SOURCE", "")
}

func (p *Bean) IsEmpty(key string) bool {
	f := true
	if p.Contains(key) {
		value := p.Get(key)
		switch value.(type) {
		case string:
			f = len(value.(string)) == 0
		case int, int8, int16, int32, int64:
			f = value.(int) == 0
		case float32, float64:
			f = value.(float32) == 0
		default:
			f = false
		}
	}
	return f
}

func (p *Bean) IsNotEmpty(key string) bool {
	return !p.IsEmpty(key)
}

func (p *Bean) Clone() *Bean {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(p); err != nil {
		fmt.Println("clone has err:", err)
		return nil
	}
	dest := NewBean("")
	gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dest)
	return dest
}

func (p *Bean) CopyOf(fields []string) *Bean {
	b := NewBean("")
	for _, field := range fields {
		b.Set(field, p.Get(field))
	}
	return b
}

func (s *Bean) Copy() *Bean {
	n := NewBean("")
	for _, k := range s.Keys() {
		n.Set(k, s.Get(k))
	}
	return n
}

func (p *Bean) Remove(key string) {
	delete(p.Data, key)
}

func (p *Bean) Size() int {
	return len(p.Data)
}
